package main

import (
	"fmt"
	"strings"
)

func main() {
	array()          //数组
	slice()          //切片
	slice_change()   //切片赋值
	slice_literals() //切片字面量
	slice_len()      //切片长度和容量
	slice_nil()      //nil切片
	make_arr()       // make动态数组
	make_cap()       // make 数组
	slice_of_slice() // 切片的切片
	slice_append()   //动态数组
	slice_range()    //切片循环
}

func array() {
	//数组的长度是其类型的一部分，因此数组不能改变大小。
	var a [2]string
	a[0] = "hello"
	a[1] = "world"
	fmt.Println(a[0], a[1]) // hello world
	fmt.Println(a)          // [hello world]
}

// 切片就像数组的引用 切片并不存储任何数据
// 切片为数组元素提供了动态大小的、灵活的视角。 在实践中，切片比数组更常用。
func slice() {
	prime := [6]int{1, 2, 3, 4, 5, 6}
	// 切片通过两个下标来界定，一个下界和一个上界，二者以冒号分隔：
	// 它会选出一个半闭半开区间，包括第一个指标，但排除最后一个元素。
	var s []int = prime[1:4] //[0, (1, 2, 3), 4, 5]
	fmt.Println(s)           // 2,3,4

	//可以利用它的默认行为来忽略上下界。 切片可以自切
	s = prime[:] //全部
	fmt.Println(s)
	s = s[1:]
	fmt.Println(s)
	s = s[:2]
	fmt.Println(s)
}

// 更改切片的元素会修改其底层数组中对应的元素。
// 和它共享底层数组的切片和数组都会观测到这些修改。
func slice_change() {
	name := [4]string{
		"John",
		"Tom",
		"Ringo",
		"Juma",
	}

	fmt.Println(name)
	a := name[0:2]
	b := name[1:3]
	fmt.Println(a, b)
	b[0] = "XXX"
	fmt.Println(a, b)
	fmt.Println(name)
}

// 切片字面量类似于没有长度的数组字面量。
func slice_literals() {
	arr := [3]bool{true, true, true}
	sar := []bool{true, true, false} //创建一个和上面相同的数组，然后再构建一个引用了它的切片
	fmt.Println(arr, "\n", sar)

	// 整数切片
	q := []int{1, 2, 3, 4, 5, 6, 7}
	fmt.Println(q)

	// 结构切片
	s := []struct {
		i int
		b bool
	}{
		{2, true},
		{4, false},
		{11, false},
	}
	fmt.Println(s)
}

// 切片的长度就是它所包含的元素个数。
// 切片的容量是从它的第一个元素开始数，到其底层数组元素末尾的个数。
func slice_len() {
	s := []int{1, 2, 3, 4, 5, 6}
	println("len =", len(s), " cap =", cap(s))

	s = s[:0] //没有元素
	println("len =", len(s), " cap =", cap(s))

}

// 切片的零值是 nil。
// nil 切片的长度和容量为 0 且没有底层数组
func slice_nil() {
	var s []int
	fmt.Println(s, "-> len=", len(s), ",cap=", cap(s))
}

// 切片可以用内置函数 make 来创建，这也是你创建动态数组的方式。
// make 函数会分配一个元素为零值的数组并返回一个引用了它的切片：
func make_arr() {
	a := make([]int, 5) // len(a) == 5
	fmt.Println(a)      // [0,0,0,0,0]
}

// // 指定它的容量，需向 make 传入第三个参数：
func make_cap() {
	a := make([]int, 0, 5) // len(a) == 0,  cap(a) == 5
	fmt.Println(a)         // []
}

// 切片可以包含任何类型，当然也包括其他切片。
func slice_of_slice() {

	// 创建一个井字棋
	board := [][]string{
		[]string{"_", "_", "_"},
		[]string{"_", "_", "_"},
		[]string{"_", "_", "_"},
	}

	// 两个玩家轮流打上 X 和 O
	board[0][0] = "X"
	board[2][2] = "O"
	board[1][2] = "X"
	board[1][0] = "O"
	board[0][2] = "X"

	for i := 0; i < len(board); i++ {
		fmt.Printf("%s\n", strings.Join(board[i], " "))
	}
}

// 为切片追加新的元素。 当 s 的底层数组太小，不足以容纳所有给定的值时，它就会分配一个更大的数组。 返回的切片会指向这个新分配的数组。
// append 的第一个参数 s 是一个元素类型为 T 的切片，其余类型为 T 的值将会追加到该切片的末尾。
// append 的结果是一个包含原切片所有元素加上新添加元素的切片。
func slice_append() {
	var s []int
	fmt.Println("len=", len(s), "cap=", cap(s), s) // len=0 cap=0 []

	// 可在空切片上追加
	s = append(s, 0)
	fmt.Println("len=", len(s), "cap=", cap(s), s) // len=1 cap=1 [0]

	// 这个切片会按需增长
	s = append(s, 1)
	fmt.Println("len=", len(s), "cap=", cap(s), s) //len=2 cap=2 [0 1]

	// 可以一次性添加多个元素
	s = append(s, 2, 3, 4, 5, 6)
	fmt.Println("len=", len(s), "cap=", cap(s), s) //len=7 cap=8 [0 1,2,3,4,5,6]
}

// for 循环的 range 形式可遍历切片或映射。
// 当使用 for 循环遍历切片时，每次迭代都会返回两个值。 第一个值为当前元素的下标，第二个值为该下标所对应元素的一份副本。
// // 可以将下标或值赋予 _ 来忽略它。
func slice_range() {
	pow := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	for i, v := range pow {
		fmt.Printf("pow[%d] == %d\n", i, v)
	}

	/*=================================================================================================================*/
	println()

	//忽略值 若你只需要索引，忽略第二个变量即可。
	for i := range pow {
		fmt.Printf("pow[%d] == ?\n", i)
	}
	println()
	//忽略下标
	for _, v := range pow {
		fmt.Printf("pow[?] == %d\n", v)
	}

}
